#include <stdio.h>
#include <stdlib.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <GL/glext.h>
#include <iostream>

#include <string>
#include <math.h>
#include "SDL/SDL.h"

#include "libraries/BM_input.h"
#include "libraries/BM_level.h"
#include "libraries/BM_anitex.h"
#include "libraries/BM_menu.h"
#include "libraries/BM_menu_object.h"
#include "libraries/BM_time.h"
#include "libraries/BM_sound.h"
#include "libraries/BM_init.h"
#include "libraries/BM_camera.h"
#include "libraries/BM_config.h"
#include "libraries/BM_light.h"
#include "player.h"

// screen width, height, and bit depth
#define SCREEN_WIDTH  800
#define SCREEN_HEIGHT 600
#define SCREEN_BPP     16

#define FPS 60

// Set up some booleans
#define TRUE  1
#define FALSE 0

enum BM_SCREENS{BM_MENU,BM_GAME};

using namespace std;

bool goto_menu = false, exit_game = false;

BMCamera Camera;
BMPlayer *player;
//SDL_Surface* aim_surface;
//BMTex aim_text;

// This is our SDL surface
SDL_Surface *screen; // screen
int cur;
BMKeyboard keyboard;
float x, y, z;
int bmGameLoop();
BMAniTex ani_tex;
BMBlock block;
BMLevel *level;
ConfigStruct config[2];
BMMenu menu;

BMLight light;
Light_Struct light1, light2, light3, light4;

int use_joystick = 0;
float sensitivity = 0.0f;

/*
GLfloat Ambient[]  = { 0.1f,  0.1f,  0.1f, 1.0f};	 // Ambient light value
GLfloat Diffuse[]  = { 1.0f,  1.0f,  1.0f, 1.0f};	 // Diffuse light value
GLfloat Position[] = {10.0f, 60.0f, 10.0f, 1.0f};	 // Light position
*/

GLfloat LightAmbient[]=		{ 0.1f, 0.1f, 0.1f, 1.0f };
GLfloat LightDiffuse[]=		{ 0.8f, 0.8f, 0.8, 1.0f };
GLfloat LightPosition[]=	{ 0.0f, 0.0f, 1.0f, 1.0f };

BMModel * player_model;

int loadModels(float x, float y, float z)
{
    player_model = new BMModel;
    player_model->initModel(MOD_OBJ, x, y, z); // init module as OBJ type

    int obj_error = 0;

    // Load our Robot Object
    if((obj_error = player_model->loadModel("Data/player.obj")) == 0)
    {
        cout << "Unable to load player model! | Error: " << obj_error << std::endl;
        return -1;
    }

    return 0;
}

int main( int argc, char **argv ){
    int videoFlags;
    SDL_Event event;
    const SDL_VideoInfo *videoInfo;
    if ( SDL_Init( SDL_INIT_EVERYTHING ) < 0 ){
	    fprintf( stderr, "Video initialization failed: %s\n",
		     SDL_GetError( ) );
	    return 1;
	}
    videoInfo = SDL_GetVideoInfo( );
    if ( !videoInfo ){
	    fprintf( stderr, "Video query failed: %s\n",
		     SDL_GetError( ) );
	    return 1;
	}
    // the flags to pass to SDL_SetVideoMode
    videoFlags  = SDL_OPENGL;          // Enable OpenGL in SDL
    videoFlags |= SDL_GL_DOUBLEBUFFER; // Enable double buffering
    videoFlags |= SDL_HWPALETTE;       // Store the palette in hardware
    //videoFlags |= SDL_RESIZABLE;       // Enable window resizing
    if ( videoInfo->hw_available )
	videoFlags |= SDL_HWSURFACE;
    else
	videoFlags |= SDL_SWSURFACE;
    if ( videoInfo->blit_hw )
	videoFlags |= SDL_HWACCEL;
    SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
    screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP,videoFlags );
    if ( !screen ){
	    fprintf( stderr,  "Video mode set failed: %s\n", SDL_GetError( ) );
	    return 1;
	}

	int error = 0;

	BMInit initClass;

    if (!(error = initClass.initGL(SCREEN_WIDTH,SCREEN_HEIGHT)))
        cout << "Error initializing OpenGL: " << error;

    // Enable Lighting
    /*
    glEnable(GL_LIGHTING);
    glEnable(GL_LIGHT0);
    glLightfv(GL_LIGHT0, GL_AMBIENT, Ambient);	// Set the ambient lighting value for Light0
    glLightfv(GL_LIGHT0, GL_DIFFUSE, Diffuse);	// Set the diffuse lighting value for Light0
    */

    // load configuration
    BMConfig configClass;
    if (!configClass.readConfig("Data/config.cfg", &config[0]))
    {
        cout << "Error opening Data/config.cfg";
        return -1;
    }

    sscanf(config[0].setting_data, "%d", &configClass.use_joystick);
    if (configClass.use_joystick != 1 && configClass.use_joystick != 0)
        return -1;
    else {
        use_joystick = configClass.use_joystick; // copy joystick setting (which is inside the config class) to our global setting
    }

    sscanf(config[1].setting_data, "%f", &configClass.sensitivity);
    if (configClass.sensitivity < 5.0f || sensitivity > 500.0f)
        return -1;
    else {
        sensitivity = configClass.sensitivity; // copy sensitivity setting (which is inside the config class) to our global setting
    }

    BMMusic theme;

    // setup SDL Mixer audio
    int audio_rate = 22050;
    Uint16 audio_format = AUDIO_S16; /* 16-bit stereo */
    int audio_channels = 2;
    int audio_buffers = 4096;

    if(Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers)) {
        printf("Unable to open audio!\n");
        return 0;
    }

    Mix_QuerySpec(&audio_rate, &audio_format, &audio_channels);

    while (1)
    {

        int loaded = theme.load("Data/theme.mp3");
        if (loaded)
            theme.play();

        goto_menu = exit_game = false;

        light.disableLightning(GL_LIGHT0);

        // load menu
        menu.loadObjects();
        int selected = menu.run_menu();

        menu.freeObjects(); // free objects

        if (loaded)
            theme.stop();
        if (loaded)
            theme.free();

        SDL_ShowCursor(SDL_DISABLE);

        switch(selected)
        {
            case 0: // exit menu
                exit_game = true;
            break;
            case 1:
                // start our game :D
                light.enableLightning(GL_LIGHT0); // enable lightning for LIGHT0
                light.createLight(GL_LIGHT0, GL_AMBIENT,255.0/255.0,  232.0/255.0,  127.0f/255.0, 1.0f, &light1); // Set the ambient lighting value for Light0
                light.createLight(GL_LIGHT0, GL_DIFFUSE, 1.0f, 1.0f, 1.0f, 1.0f, &light2); // Set the diffuse lighting value for Light0
                light.createLight(GL_LIGHT0, GL_SPECULAR, 5.0f,  5.0f,  5.0f, 1.0f, &light3); // Set the specular lighting value for Light0
                // x - y - z
                light.createPositionLight(GL_LIGHT0, 10.0f,10.0f, 25.0f, 1.0f, &light4); // Set the position lighting value for Light0

                /*glEnable(GL_LIGHT0);
                glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient);		// Setup The Ambient Light
                glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse);		// Setup The Diffuse Light
                glLightfv(GL_LIGHT0, GL_POSITION,LightPosition);	// Position The Light
                */

                //glColor4f(1.0f, 1.0f, 1.0f, 0.5);					// Full Brightness.  50% Alpha
                //glBlendFunc(GL_SRC_ALPHA,GL_ONE);					// Set The Blending Function For Translucency

                // load level
				level = new BMLevel;
				player = new BMPlayer;
				
                level->load("data/level/level1.lvl");
				player->load("Data/player.obj");

                // Camera

                float scor[3];
                level->getStart(0,&scor[0],&scor[1],&scor[2]);
				player->resetView();
				player->thirdPerson();
				player->move(scor[0], scor[1] + 0.1, scor[2] );
                //Camera.reset();
                //Camera.Move( F3dVector(scor[0], scor[1], scor[2] ));

                //level.loadSmile();
                //level.move(0.0f, 0.0f,-500.0f);
                cur = BM_MENU;
                x = 0;
                y = 0;
                z = 0;

                //SDL_ShowCursor(SDL_DISABLE);

                loadModels(player->getX(), player->getY(), 0.0f);

               // player.move(player_model->getX(), player_model->getY(), 10.0f);

				level->begin();
                // wait for events
                while ( bmGameLoop() ){
                    while ( SDL_PollEvent( &event ) ){
                        if( event.type == SDL_QUIT)
                        {
                            exit_game = true;
                            break;
                        }

						if( event.type == SDL_MOUSEMOTION )
							player->look(event,SCREEN_WIDTH,SCREEN_HEIGHT,use_joystick,sensitivity);

                        // otherwise if joystick is being used..
                    }

                    if (exit_game)
                    {
                        break;
                    }
                }

                // free level
				level->end();
                player_model->freeModel();
                delete player_model;
                delete player;
                delete level;
            break;
            case 2:
            break;
            case 3:
            break;
            case 4:
                exit_game = true;
            break;
        }

        if (exit_game)
            break;
    }

    // close sdl mixer audio
    Mix_CloseAudio();

    // clean ourselves up and exit
    SDL_Quit();

    // Should never get here
    return 0;
}

int bmGameLoop(){
	BMTimer timer;
    timer.setFPS(FPS);
	timer.start();
	keyboard.poll();
    glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	player->resetDirection();

    if(keyboard.hold(SDLK_q)) {
        player->jump(level);
	}
    if(keyboard.hold(SDLK_DOWN) || keyboard.hold(SDLK_s)) {
        if (keyboard.hold(SDLK_s)) {
            player->back();
            if(player->levelCollision(level)){
                player->forward();
            }
        }
    }
    if((keyboard.hold(SDLK_UP) || keyboard.hold(SDLK_w))) {
		if (keyboard.hold(SDLK_w)) {
            player->forward();
            if(player->levelCollision(level)){
                player->back();
            }
        }
    }
    if(keyboard.hold(SDLK_LEFT) || keyboard.hold(SDLK_a)) {
        if (keyboard.hold(SDLK_a)) {
            player->left();
            if(player->levelCollision(level)){
                player->right();
            }
        }

    }
    if(keyboard.hold(SDLK_RIGHT) || keyboard.hold(SDLK_d)) {
        if (keyboard.hold(SDLK_d)) {
            player->right();
            if(player->levelCollision(level)){
                player->left();
            }

        }
	}

    if(keyboard.hold(SDLK_ESCAPE)) {
        goto_menu = true;
    }

    glLoadIdentity();
	player->view();
	player->draw();

    light.renderLight(&light1);
    light.renderLight(&light2);
    light.renderLight(&light3);
    light.renderLight(&light4);

    level->draw();

    SDL_GL_SwapBuffers( );
	timer.frameDelay();
	if (goto_menu) // go back to menu
        return 0;

	return 1;
}
